package se.perp.c4.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import se.perp.c4.model.Actor.Side;
import se.perp.c4.model.Territory.ETerritory;
import static se.perp.c4.model.Territory.ETerritory.*;

public class TerritoryCollection implements Iterable<Territory> {

    private Map<Territory.ETerritory, Territory> terrs =
            new HashMap<Territory.ETerritory, Territory>();

    public TerritoryCollection() {
        terrs.put(SHIRE, TerritoryFactory.create(SHIRE));
        terrs.put(ARTHEDAIN, TerritoryFactory.create(ARTHEDAIN));
        terrs.put(CARDOLAN, TerritoryFactory.create(CARDOLAN));
        terrs.put(RHUDAUR, TerritoryFactory.create(RHUDAUR));
        terrs.put(EREGION, TerritoryFactory.create(EREGION));
        terrs.put(ENEDWAITH, TerritoryFactory.create(ENEDWAITH));
        terrs.put(HIGHPASS, TerritoryFactory.create(HIGHPASS));
        terrs.put(MISTYMTS, TerritoryFactory.create(MISTYMTS));
        terrs.put(MORIA, TerritoryFactory.create(MORIA));
        terrs.put(GAPOFROHAN, TerritoryFactory.create(GAPOFROHAN));
        terrs.put(MIRKWOOD, TerritoryFactory.create(MIRKWOOD));
        terrs.put(FANGORN, TerritoryFactory.create(FANGORN));
        terrs.put(ROHAN, TerritoryFactory.create(ROHAN));
        terrs.put(DAGORLAD, TerritoryFactory.create(DAGORLAD));
        terrs.put(GONDOR, TerritoryFactory.create(GONDOR));
        terrs.put(MORDOR, TerritoryFactory.create(MORDOR));
    }

    public static ETerritory[] placementTerritories(Side side) {
        if (side == Side.LIGHT) {
            return new ETerritory[]{SHIRE, ARTHEDAIN, CARDOLAN, RHUDAUR, EREGION, ENEDWAITH};
        } else {
            return new ETerritory[]{MORDOR, DAGORLAD, GONDOR, MIRKWOOD, FANGORN, ROHAN};
        }
    }

    public Territory get(ETerritory which) {
        return terrs.get(which);
    }

    public Iterator<Territory> iterator() {
        return terrs.values().iterator();
    }

    public List<ETerritory> getValidSidewaysRetreats(Actor actor, ETerritory eFrom) {
        List<ETerritory> ret = new ArrayList<ETerritory>();

        Territory from = get(eFrom);

        ETerritory[] retreats = from.sidewaysNeighbors;
        for (ETerritory et : retreats) {
            Territory to = get(et);
            boolean validPassage = to.getOccupantCount() == 0 ||
                    (to.isOccupiedByDark() && actor.isDark()) ||
                    (to.isOccupiedByLight() && actor.isLight());

            if (!to.isFull() && validPassage) {
                ret.add(et);
            }
        }

        return ret;
    }

    public List<ETerritory> getValidBackwardsRetreats(Actor actor, ETerritory from) {
        List<ETerritory> ret = new ArrayList<ETerritory>();
        for (Territory t : terrs.values()) {
            if (t.hasLightNeighbor(from)) {

                // River and tunnel don't count.
                if ((from == FANGORN && t.which == EREGION) ||
                        (from == ROHAN && t.which == FANGORN) ||
                        (from == FANGORN && t.which == MIRKWOOD)) {
                    continue;
                }

                boolean validPassage = t.getOccupantCount() == 0 ||
                        (t.isOccupiedByDark() && actor.isDark()) ||
                        (t.isOccupiedByLight() && actor.isLight());

                if (!t.isFull() && validPassage) {
                    ret.add(t.which);
                }
            }
        }

        return ret;
    }

    public void moveActor(Actor a, ETerritory eTo) {
        ETerritory eFrom = a.where;
        assert eFrom != null;
        if (eFrom != ETerritory.NOWHERE) {
            Territory from = get(eFrom);
            from.removeActor(a);
        }
        Territory to = get(eTo);
        to.addActor(a);
    }

    public boolean canPlayPalantir() {
        for (Territory t : terrs.values()) {
            if (t.isOccupiedByLight() && t.which != SHIRE) {
                return true;
            }
        }
        return false;
    }

    public boolean canPlayRecallToMordor() {
        if (terrs.get(MORDOR).isFull() || terrs.get(MORDOR).isOccupiedByLight()) {
            return false;
        }

        for (Territory t : terrs.values()) {
            if (t.isOccupiedByDark() && t.which != MORDOR) {
                return true;
            }
        }
        return false;
    }

    public boolean canPlayGandalfWhite() {
        for (Territory t : terrs.values()) {
            if (t.isOccupiedByLight()) {
                for (Actor a : t.getOccupants()) {
                    if (a.who == Actor.EActor.GANDALF) {
                        return false;
                    }
                }
            }
        }

        Territory fangorn = terrs.get(FANGORN);
        return !fangorn.isFull() && !fangorn.isOccupiedByDark();
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        for (Territory t : terrs.values()) {
            sb.append(t.toString() + "\n");
        }
        return sb.toString();
    }

    public void emptyAll() {
        for (Territory t : terrs.values()) {
            t.clearOccupants();
        }
    }
}
